Skip to content

unittests now use a mock ray implementation#127

Open
engintoklu wants to merge 3 commits into
masterfrom
fix/ray-local-mode
Open

unittests now use a mock ray implementation#127
engintoklu wants to merge 3 commits into
masterfrom
fix/ray-local-mode

Conversation

@engintoklu

@engintoklu engintoklu commented Jun 3, 2026

Copy link
Copy Markdown
Collaborator

For the unit tests, a mock implementation of ray is introduced, as a replacement for the now-obsolete local mode of the actual ray library.

Summary by CodeRabbit

  • New Features

    • Added a lightweight mock Ray implementation and a deterministic ActorPool for tests.
  • Tests

    • Tests now inject the mock Ray to avoid spawning processes and ensure isolation.
    • Deterministic seeding extended to torch/random/numpy for reproducible runs.
    • MLflow test logging switched to a SQLite tracking URI.

For the unit tests, a mock implementation of
'ray' is introduced, as a replacement for the
now-obsolete local mode of the actual 'ray'
library.
@engintoklu engintoklu self-assigned this Jun 3, 2026
@coderabbitai

coderabbitai Bot commented Jun 3, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 78e818da-6ea1-4dc9-8109-482658b1af04

📥 Commits

Reviewing files that changed from the base of the PR and between 04b056d and 2670f6f.

📒 Files selected for processing (3)
  • tests/_mock-site-packages/ray/__init__.py
  • tests/_mock-site-packages/ray/util.py
  • tests/conftest.py
🚧 Files skipped from review as they are similar to previous changes (2)
  • tests/_mock-site-packages/ray/init.py
  • tests/_mock-site-packages/ray/util.py

📝 Walkthrough

Walkthrough

Adds a lightweight mock Ray package for tests: a core mock API with serialization and remote dispatch, an ActorPool utility, and test wiring to use the mock instead of initializing real Ray.

Changes

Mock Ray Testing Framework

Layer / File(s) Summary
Mock Ray Core API Implementation
tests/_mock-site-packages/ray/core.py
Foundational mock APIs: pickle/cloudpickle fallback, init/is_initialized/shutdown state, serialization helpers, ObjectRef, _RemoteMethod/_RemoteInstance/_RemoteCaller remote dispatch, public remote() (enforces initialization) and get() for result retrieval.
Ray Package Module Setup
tests/_mock-site-packages/ray/__init__.py
Package-level module documented as non-process-creating local-mode replacement; re-exports ObjectRef, init, is_initialized, remote, get, shutdown from core and exposes util; defines __all__.
ActorPool Utility Implementation
tests/_mock-site-packages/ray/util.py
Mock ActorPool with class SEED = 1; map() assigns objects to actors round-robin and applies fn(actor, obj), map_unordered() shuffles map() results with seeded RNG and uses core.get to return values.
Test Suite Configuration
tests/conftest.py
Prepends _mock-site-packages to sys.path and imports mock ray; adds torch.manual_seed(SEED) to session seeding; removes the in-hook ray.init(...) (disabled) while keeping ray.shutdown() on session finish.
MLflow Test Tracking URI Change
tests/test_logging.py
test_mlflow_logger now sets MLflow tracking URI to a SQLite DB file (sqlite:///{tmpdir}/tmp.db) instead of the prior file://{tmpdir} URI.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 A rabbit hops through testing's maze,
With mocks that dodge Ray's cloudy haze!
No processes spawn, just pickles and cheer,
Remote calls return without frontier.
Hop—deterministic tests appear!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 76.19% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: introducing a mock ray implementation for unit tests to replace Ray's obsolete local mode.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/ray-local-mode

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
tests/conftest.py (1)

27-30: ⚡ Quick win

Add a fail-fast check to ensure the mock ray package is imported

Within this repo, only tests/conftest.py imports ray, so normal pytest collection shouldn’t preload the real Ray before the mock-path insertion. Still, adding the guard prevents silent fallback if ray was imported earlier by external code/plugins, keeping the PR’s test wiring robust.

Suggested guard
 _TESTS_DIR = os.path.dirname(os.path.abspath(__file__))
-sys.path.insert(0, os.path.join(_TESTS_DIR, "_mock-site-packages"))
+_MOCK_SITE_PACKAGES = os.path.join(_TESTS_DIR, "_mock-site-packages")
+sys.path.insert(0, _MOCK_SITE_PACKAGES)
 
 import ray  # noqa: E402
+
+if not os.path.realpath(getattr(ray, "__file__", "")).startswith(os.path.realpath(_MOCK_SITE_PACKAGES)):
+    raise RuntimeError(f"Expected mock ray from {_MOCK_SITE_PACKAGES}, got {getattr(ray, '__file__', None)}")
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/conftest.py` around lines 27 - 30, After inserting the mock path into
sys.path, ensure the imported ray is the mock by verifying its module origin; in
tests/conftest.py after sys.path.insert(...) and the import ray statement, check
ray.__file__ or importlib.util.find_spec("ray").origin contains
"_mock-site-packages" or the _TESTS_DIR path and if not raise a clear
ImportError (or call pytest.exit) so the test run fails fast when the real Ray
is loaded instead of the mock.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tests/_mock-site-packages/ray/util.py`:
- Around line 40-76: The mock eagerly materializes results but should preserve
Ray semantics by returning iterators: change ActorPool.map to be a generator
that yields each processed result lazily (for each object compute i_actor =
i_obj % len(self.__actors), call fn(actor, obj) and yield get(...) immediately
rather than appending to a list), and change map_unordered to return a generator
that yields results in a shuffled order without precomputing all outputs (e.g.,
determine the sequence of actor/object pairs, shuffle the sequence using
self.__generator.shuffle, then iterate that shuffled sequence yielding
get(fn(actor,obj)) for each); update usages of map/map_unordered to expect
iterators instead of lists and keep references to __actors, __generator, fn, and
get to locate the implementation.

---

Nitpick comments:
In `@tests/conftest.py`:
- Around line 27-30: After inserting the mock path into sys.path, ensure the
imported ray is the mock by verifying its module origin; in tests/conftest.py
after sys.path.insert(...) and the import ray statement, check ray.__file__ or
importlib.util.find_spec("ray").origin contains "_mock-site-packages" or the
_TESTS_DIR path and if not raise a clear ImportError (or call pytest.exit) so
the test run fails fast when the real Ray is loaded instead of the mock.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 40afff65-58b3-48f5-a1e0-201d37e01e03

📥 Commits

Reviewing files that changed from the base of the PR and between cebcac4 and 5b07b66.

📒 Files selected for processing (4)
  • tests/_mock-site-packages/ray/__init__.py
  • tests/_mock-site-packages/ray/core.py
  • tests/_mock-site-packages/ray/util.py
  • tests/conftest.py

Comment thread tests/_mock-site-packages/ray/util.py Outdated
Comment on lines +40 to +76
def map(self, fn: Callable[[_RemoteInstance, Any], Any], objects: Sequence[Any]) -> list:
"""
Pair actors with objects, and for each pair, call `fn(actor, object)`.

Args:
fn: The function which specifies how an object is to be used
with its associated actor.
objects: A sequence (e.g. list or tuple) of object to be processed
by the pool of actors.

Returns:
Processed counterparts of the objects, as a list of the same length
as `objects`.
"""
num_actors = len(self.__actors)
result = []
for i_obj, obj in enumerate(objects):
i_actor = i_obj % num_actors
actor = self.__actors[i_actor]
result.append(fn(actor, obj))
return get(result)

def map_unordered(self, fn: Callable[[_RemoteInstance, Any], Any], objects: Sequence[Any]) -> list:
"""
Like the `map` method, but the results are shuffled.

The reasoning behind the shuffling is to mimic the fact that
`map_unordered` of the actual 'ray' library returns the processed
results in an undeterministic order.

Also note that, for reproducibility during tests, the shuffling
depends on a constant random seed, which is stored by class-level
constant `SEED`.
"""
result = self.map(fn, objects)
self.__generator.shuffle(result)
return result

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Locate and display the full ActorPool mock implementation
FILE="tests/_mock-site-packages/ray/util.py"
echo "== $FILE =="
nl -ba "$FILE" | sed -n '1,220p'

# Find usages of ActorPool.map/map_unordered in the repo
echo "== rg usages of ActorPool.map / map_unordered =="
rg -n "ActorPool\.map_unordered|ActorPool\.map\(" . || true

# Also search for 'map_unordered(' and 'map(' where it might be called on ActorPool instance variable
echo "== rg for '.map_unordered(' =="
rg -n "\.map_unordered\(" . || true
echo "== rg for '.map(' (narrowed to ActorPool variable patterns) =="
rg -n "\.map\(" . | head -n 200 || true

Repository: nnaisense/evotorch

Length of output: 167


🌐 Web query:

Ray ActorPool.map returns generator iterator return type map_unordered unordered iterator yields results as they complete

💡 Result:

In Ray, both ray.util.ActorPool.map and ray.util.ActorPool.map_unordered return a Python generator iterator [1][2][3]. The key distinction between the two methods lies in the ordering of the returned results [1][2]: - ray.util.ActorPool.map returns an ordered iterator that yields results corresponding to the input list order, as they finish [1]. - ray.util.ActorPool.map_unordered returns an unordered iterator that yields results as they complete [2][4]. Because map_unordered does not enforce any ordering constraints, it can be more efficient than map in scenarios where tasks have variable execution times, as it allows results to be processed as soon as they become available rather than waiting for preceding tasks in the input sequence to complete [2][4]. For both methods, it is important to note that you must iterate over the returned iterator to actually trigger and consume the computation [1][5]. If you simply call the method without iterating, the tasks may not be fully executed or the results may not be retrieved as expected [1].

Citations:


🌐 Web query:

rAY util ActorPool map_unordered documentation returns generator iterator list

💡 Result:

Ray’s ray.util.ActorPool.map_unordered(...) returns an iterator (a generator object) that yields results “as they finish”, i.e., unordered with respect to the input order. [1] Concretely, the implementation defines an inner get_generator() that does while self.has_next(): yield self.get_next_unordered() and then returns get_generator(); so you can iterate it directly or consume it (e.g., list(pool.map_unordered(...))). [2] The docs for map_unordered describe this as “returning an unordered iterator” whose yielded results are produced as tasks complete, which may be more efficient than ordered map() when some computations take longer. [1]

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Locate and display the full ActorPool mock implementation
FILE="tests/_mock-site-packages/ray/util.py"
echo "== $FILE =="
nl -ba "$FILE" | sed -n '1,220p'

# Find usages of ActorPool.map/map_unordered in the repo
echo "== rg usages of ActorPool.map / map_unordered =="
rg -n "ActorPool\.map_unordered|ActorPool\.map\(" . || true

# Also search for 'map_unordered(' and 'map(' where it might be called on ActorPool instance variable
echo "== rg for '.map_unordered(' =="
rg -n "\.map_unordered\(" . || true
echo "== rg for '.map(' (narrowed to ActorPool variable patterns) =="
rg -n "\.map\(" . | head -n 200 || true

Repository: nnaisense/evotorch

Length of output: 167


Preserve Ray iterator semantics in the ActorPool mock.

Ray’s ray.util.ActorPool.map / map_unordered return iterator/generator objects (lazy; results are produced as the iterator is consumed, with map_unordered yielding as tasks complete). This mock eagerly materializes a full list in map, and map_unordered shuffles only after fully realizing the list, so partial iteration / next(...)-style consumption won’t match Ray behavior. https://docs.ray.io/en/latest/ray-core/api/doc/ray.util.ActorPool.map.html https://docs.ray.io/en/latest/ray-core/api/doc/ray.util.ActorPool.map_unordered.html

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/_mock-site-packages/ray/util.py` around lines 40 - 76, The mock eagerly
materializes results but should preserve Ray semantics by returning iterators:
change ActorPool.map to be a generator that yields each processed result lazily
(for each object compute i_actor = i_obj % len(self.__actors), call fn(actor,
obj) and yield get(...) immediately rather than appending to a list), and change
map_unordered to return a generator that yields results in a shuffled order
without precomputing all outputs (e.g., determine the sequence of actor/object
pairs, shuffle the sequence using self.__generator.shuffle, then iterate that
shuffled sequence yielding get(fn(actor,obj)) for each); update usages of
map/map_unordered to expect iterators instead of lists and keep references to
__actors, __generator, fn, and get to locate the implementation.

@codecov

codecov Bot commented Jun 3, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 75.36%. Comparing base (cebcac4) to head (2670f6f).

Additional details and impacted files
@@            Coverage Diff             @@
##           master     #127      +/-   ##
==========================================
- Coverage   75.43%   75.36%   -0.08%     
==========================================
  Files          59       59              
  Lines        9556     9556              
==========================================
- Hits         7209     7202       -7     
- Misses       2347     2354       +7     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant